// poll_server.hpp封装poll服务器
#pragma once
#include "Socket.hpp"
#include <poll.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD -1
class PollServer
{
public:
PollServer(int port)
        : _port(port)
    {
    }
    void InitPollServer() // 初始化
    {
        _LisSocFd = Socket::SocCreate();
        Socket::SocketBind(_LisSocFd, _port);
        Socket::SocketListen(_LisSocFd, BACK_LOG);
    }
    ~PollServer()
    {
        if (_LisSocFd >= 0)
        {
            close(_LisSocFd);
        }
    }
    void HandlerEvent(struct pollfd fds[], int num) // 事件就绪之后的处理
    {
        for (int i = 0; i < num; i++)//遍历保存着需要被poll监视的文件描述符的数组
        {
            if (fds[i].fd == DFL_FD)//跳过无效的文件描述符
                continue;
            if (fds[i].fd == _LisSocFd &&fds[i].revents&POLLIN)//对处于就绪状态的监听套接字的处理
            {
                struct sockaddr_in peer;
                memset(&peer, 0, sizeof(peer));
                socklen_t len = sizeof(peer);
                int sockFd = accept(_LisSocFd, (struct sockaddr*)&peer, &len);//获取连接
                if (sockFd < 0)
                {
                    std::cerr << "accept error" << std::endl;
                    continue;
                }
                std::string peer_ip = inet_ntoa(peer.sin_addr);
                int peer_port = ntohs(peer.sin_port);
                std::cout << "Get a link sucessfully!" << peer_ip << ":" << peer_port << std::endl;
                 //将获取到的套接字添加到fds数组中，并关心其读事件
                //如果将该文件描述符无法添加进数组直接关闭该文件描述符
                if (!SetPollfds(fds, NUM, sockFd))
                {
                    close(sockFd);
                    std::cout << "select server is full,close fd:" << sockFd << std::endl;
                }
            }
            else if (fds[i].revents&POLLIN)//读事件就绪
            {
                char buffer[1024];
                ssize_t s = read(fds[i].fd, buffer, sizeof(buffer) - 1);
                if (s > 0)//读取成功
                {
                    buffer[s] = '\0';
                    std::cout << "echo#:" << buffer << std::endl;
                }
                else if (s == 0)//读事件就绪，但是在读的时候没有读到，只能是对端的连接关闭
                {
                    std::cout << "Client quit!" << std::endl;
                    close(fds[i].fd);
                    UnSetPollfds(fds,i);
                }
                else
                {
                    std::cerr << "read error" << std::endl;
                    close(fds[i].fd);
                    UnSetPollfds(fds,i);
                }
            }
        }
    }
    void Run()
    {
        struct pollfd fds[NUM];
        ClearPollfds(fds,NUM,DFL_FD);
        SetPollfds(fds,NUM,_LisSocFd);
        for(;;)
        {
            switch(poll(fds,NUM,-1)){
                case 0:
                    std::cout<<"time out..."<<std::endl;
                    break;
                case -1:
                    std::cerr<<"poll error"<<std::endl;
                    break;
                default:
                    HandlerEvent(fds,NUM);
                    break;
            }
        }
    }

private:
    void ClearPollfds(struct pollfd fds[],int num,int default_fd)//初始化fds
    {
        for(int i=0;i<num;i++)
        {
            fds[i].fd=default_fd;
            fds[i].events=0;
            fds[i].revents=0;
        }
    }
    bool SetPollfds(struct pollfd fds[],int num,int fd)//将文件描述符添加至fds中
    {
        for(int i=0;i<num;i++)
        {
           if(fds[i].fd==DFL_FD)
           {
            fds[i].fd=fd;
            fds[i].events |= POLLIN;//设置读事件关心
            return true;
           }
        }
        return false;//fds数据已满
    }
    void UnSetPollfds(struct pollfd fds[],int pos)//移除fds中的文件描述符
    {
        fds[pos].fd=DFL_FD;
        fds[pos].events=0;
        fds[pos].revents=0;
    }
private:
    int _port;
    int _LisSocFd;
};